﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Security.Cryptography;

namespace Server
{
    public static class Encryption
    {
        /* Kryptering med Rijndael-algoritme med en 256-bit kryptering
         * Kilde: http://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp */
        /* This constant string is used as a "salt" value for the PasswordDeriveBytes function calls.
         * This size of the IV (in bytes) must = (keysize / 8).  Default keysize is 256, so the IV must be
         * 32 bytes long.  Using a 16 character string here gives us 32 bytes when converted to a byte array.
         * */
        private const string InitVector = "tu89geji340t89u2"; //Salt-værdi

        // This constant is used to determine the keysize of the encryption algorithm.
        private const int Keysize = 256;

        public static string Encrypt(string plainText, string passPhrase)
        {
            byte[] InitialVectorBytes = Encoding.UTF8.GetBytes(InitVector); //Salt-værdien i et byte-array
            byte[] PlainTextBytes = Encoding.UTF8.GetBytes(plainText); //Teksten som skal krypteres i et byte-array

            PasswordDeriveBytes Password = new PasswordDeriveBytes(passPhrase, null); //Password-type til krypteringen
            byte[] PasswordBytes = Password.GetBytes(Keysize / 8); // ?????????????

            RijndaelManaged symmetricKey = new RijndaelManaged(); //Laves en ny instans af selve krypteringsalgoritmen
            symmetricKey.Mode = CipherMode.CBC; //Sættes til en mode, som laver en ny krypteret værdi af den samme string, da den blander hver block sammen med det forrige den lige har krypteret
            ICryptoTransform Encryptor = symmetricKey.CreateEncryptor(PasswordBytes, InitialVectorBytes); //Her laves selve encrypteren, ved at give den passwordet, og saltværdien.
            MemoryStream EncryptedStringStream = new MemoryStream(); //Streamen hvor det krypterede data kommer til at ligge.
            CryptoStream CryptoStream = new CryptoStream(EncryptedStringStream, Encryptor, CryptoStreamMode.Write); //En stream der kan bruges til at smide noget i - så krypterer den det, og sender det videre til EncryptedStringStream

            CryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length); //.Write gør at den krypterer det, og sender det videre til EncryptedStringStream
            CryptoStream.FlushFinalBlock(); //Flusher CryptoStream så den i princippet er klar til at kryptere ny tekst.

            byte[] EncryptedStringBytes = EncryptedStringStream.ToArray(); //Laver et bytearray ud af EncryptedStringStream (kan sendes over netværk osv?)

            EncryptedStringStream.Close();
            CryptoStream.Close(); //Lukker de to streams - burde ikke være nødvendigt?
            return Convert.ToBase64String(EncryptedStringBytes, 0, EncryptedStringBytes.Length); //Den krypterede string returneres
        }

        public static string Decrypt(string encryptedString, string passPhrase)
        {
            byte[] InitVectorBytes = Encoding.ASCII.GetBytes(InitVector); //Salt-værdien i et byte-array
            byte[] EncryptedStringBytes = Convert.FromBase64String(encryptedString); //Input-stringen puttes i et byte-array

            PasswordDeriveBytes EncryptionPassword = new PasswordDeriveBytes(passPhrase, null); //Input-passwordet puttes i en PasswordDeriveBytes-type
            byte[] PasswordBytes = EncryptionPassword.GetBytes(Keysize / 8); //?????????????

            RijndaelManaged SymmetricKey = new RijndaelManaged();
            SymmetricKey.Mode = CipherMode.CBC;
            ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(PasswordBytes, InitVectorBytes);
            MemoryStream EncryptedStringStream = new MemoryStream(EncryptedStringBytes);
            CryptoStream CryptoStream = new CryptoStream(EncryptedStringStream, Decryptor, CryptoStreamMode.Read); //Cryptostream bruges til at udføre  kryptologiske transformationer på en stream

            byte[] DecryptedPlainTextBytes = new byte[EncryptedStringBytes.Length];
            int DecryptedByteCount = CryptoStream.Read(DecryptedPlainTextBytes, 0, DecryptedPlainTextBytes.Length);

            EncryptedStringStream.Close();
            CryptoStream.Close();
            return Encoding.UTF8.GetString(DecryptedPlainTextBytes, 0, DecryptedByteCount);
        }
    }
}
